Virtualbox + CentOS7 + シリアル接続してみる
Virutalbox上でLinuxを動かす際に起動ログやカーネルパニック時の画面ログを取りたい時があります。
調べると仮想シリアルポートを組み合わせることでVritualbox上の仮想マシンとシリアル接続が可能らしい。
イメージはこんな感じ。
ということで早速やってみました。
Windos7のVirtualbox上でCentOS7を起動しTeratermからシリアル接続して起動ログが表示されるか確認します。
仮想シリアルポートの導入
まずは、Windows側にここからソフトをダウンロードしてインストールします。
インストール後、Teratermを起動してCOMポートが2つ増えていればOKです。
動作確認としてTeratermを2つ起動し、各COMポート(上絵ではCOM4,COM5)を開いて片方の端末から入力した文字がもう一方の端末に表示されるか確認してみます。
動作確認がとれたら、Virutualboxの設定でシリアルポートにインストールしたCOMポート(今回の例ではCOM4)を割り当てます。
TeratermでCOMポートを開いているとエラーになるので事前に開放しておくこと。
ホスト側の設定は以上です。
grub2の設定ファイルを編集
続いてゲスト側の設定です。
シリアルの準備が出来たら、次はCentOS7を起動しgrub2の設定ファイル(/etc/default/grub)を開いて、画面ログをコンソールに出力するように編集します。
GRUB_CMDLINE_LINUXの行の"rhgb quiet"を消すと起動時のログが省略されずに画面に出力されるようになり、console=xxxで出力先のデバイスとボーレートを指定します。
修正前
[root@vm1 ~]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
GRUB_DISABLE_RECOVERY="true"
修正後
[root@vm1 ~]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap console=tty0 console=ttyS0, 115200"
GRUB_DISABLE_RECOVERY="true"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"
ファイルを修正したら下記コマンドでgrub2を更新します。
[root@vm1 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
これで準備が整いました。
実行結果
Teratermを起動しシリアルポート(今回の例ではCOM5)を開きます。
ゲストOSを再起動してTeraterm上に画面ログが出力すれば成功です。
無事シリアルにログが表示されました。
シリアル端末からログイン、コマンド入力等の操作も問題なく行えます。
packetソケットでキャプチャしてみる
Linuxのpacketソケットを使って受信フレームの内容を表示してみます。
今回はEther + IPv4 + TCPで受信したフレームの各ヘッダ情報を表示するアプリケーションを作ります。Linux, C言語です。
packetソケットについての詳しい説明はMan page of PACKETとかを参照で。
L2/L3/L4ヘッダの構造体
各ヘッダのフォーマットと使用する構造体を確認しておきます。
・L2ヘッダ(Ethernet)
#include <net/ethernet.h> struct ether_header { u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */ u_int16_t ether_type; /* packet type ID field */ } __attribute__ ((__packed__));
・L3ヘッダ(IPv4)
#include <netinet/ip.h> struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; unsigned int ihl:4; #else # error "Please fix <bits/endian.h>" #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ };
・L4ヘッダ(TCP)
#include <netinet/tcp.h> struct tcphdr { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq; # if __BYTE_ORDER == __LITTLE_ENDIAN u_int16_t res1:4; u_int16_t doff:4; u_int16_t fin:1; u_int16_t syn:1; u_int16_t rst:1; u_int16_t psh:1; u_int16_t ack:1; u_int16_t urg:1; u_int16_t res2:2; # elif __BYTE_ORDER == __BIG_ENDIAN u_int16_t doff:4; u_int16_t res1:4; u_int16_t res2:2; u_int16_t urg:1; u_int16_t ack:1; u_int16_t psh:1; u_int16_t rst:1; u_int16_t syn:1; u_int16_t fin:1; # else # error "Adjust your <bits/endian.h> defines" # endif u_int16_t window; u_int16_t check; u_int16_t urg_ptr; };
ソースコード
フレームを3回受信してヘッダを表示したら終了するサンプルです。
socket作成時にprotocolの引数にhtons(ETH_P_IP)を渡してPv4の場合のみ受信する指定をしてます。
packet.c
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <linux/if_packet.h> #include <net/ethernet.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <errno.h> #include <stdint.h> #define MAX_EVENTS 10 #define PERROR(X) \ {\ char __strerr[128] = {0};\ int errcode = errno;\ \ strerror_r(errcode, __strerr, sizeof(__strerr));\ printf(X " failed(%d:%s)\n", errcode, __strerr);\ } int main(int argc, char *argv) { int ret = 0; int rc = 0; int sfd_raw = -1; /* raw socketを用意 */ sfd_raw = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); if (sfd_raw < 0) { PERROR("socket"); goto error_end; } int epollfd = -1; int nfds = -1; struct epoll_event events[MAX_EVENTS]; struct epoll_event evt = { .events = EPOLLIN, .data = { .fd = sfd_raw, }, }; epollfd = epoll_create(MAX_EVENTS); if (epollfd < 0) { PERROR("epoll_create()"); close(sfd_raw); goto error_end; } rc = epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd_raw, &evt); if (rc != 0) { PERROR("epoll_ctl()"); close(sfd_raw); close(epollfd); goto error_end; } #define FRAME_COUNT_MAX (3) int frame_cnt = 0; for(;;) { nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds < 0) { PERROR("epoll_wait()"); break; } int fd = 0; for (fd = 0; fd < MAX_EVENTS; fd++) { if (events[fd].data.fd == sfd_raw) { /* try to recieve */ uint8_t buf[65535] = {0}; ssize_t sz = recv(sfd_raw, &buf, sizeof(buf), 0); if (sz < 0) { PERROR("recv"); close(sfd_raw); close(epollfd); goto error_end; } else if (sz == 0) { /* what's the matter ? */ printf("unknown event was happend.\n"); break; } else { /* 受信フレームの表示(IPv4 + TCPのみ) */ printf("frame:%d %d bytes recieved\n", frame_cnt+1, sz); /* Ether Header */ struct ether_header *eth = (struct ether_header *) buf; printf("======== ETHERNET HEADER ========\n"); printf("Destination MAC Address = %02x:%02x:%02x:%02x:%02x:%02x\n", eth->ether_dhost[0], eth->ether_dhost[1], eth->ether_dhost[2], eth->ether_dhost[3], eth->ether_dhost[4], eth->ether_dhost[5]); printf("Source MAC Address = %02x:%02x:%02x:%02x:%02x:%02x\n", eth->ether_shost[0], eth->ether_shost[1], eth->ether_shost[2], eth->ether_shost[3], eth->ether_shost[4], eth->ether_shost[5]); printf("Ethernet Type = 0x%04x\n", ntohs(eth->ether_type)); /* IP Header */ if (ntohs(eth->ether_type) == ETHERTYPE_IP) { struct iphdr* ip = (struct iphdr *)(eth + 1); printf("======== IP HEADER ========\n"); printf("Version = %u\n", ip->version); printf("IHL = %u(%u bytes)\n", ip->ihl, ip->ihl*32); printf("Type of Service = 0x%02x[%u%u%u%u%u%u%u%u]\n", ip->tos, (ip->tos >> 7) & 0x01, (ip->tos >> 6) & 0x01, (ip->tos >> 5) & 0x01, (ip->tos >> 4) & 0x01, (ip->tos >> 3) & 0x01, (ip->tos >> 2) & 0x01, (ip->tos >> 1) & 0x01, (ip->tos) & 0x01); printf("Total Length = %u bytes\n", ntohs(ip->tot_len)); printf("Identification = %u\n", ntohs(ip->id)); printf("Flags = 0x%02x[%u%u%u]\n", ip->frag_off, (ip->frag_off >> 2) & 0x01, (ip->frag_off >> 1) & 0x01, (ip->frag_off) & 0x01); printf("TTL = %u\n", ip->ttl); printf("Protocol = %u\n", ip->protocol); printf("Check-Sum = 0x%04x\n", ntohs(ip->check)); struct in_addr saddr; struct in_addr daddr; saddr.s_addr = ip->saddr; daddr.s_addr = ip->daddr; printf("Source Address = %s\n", inet_ntoa(saddr)); printf("Destination Address = %s\n", inet_ntoa(daddr)); /* TCP Header */ if (ip->protocol == IPPROTO_TCP) { struct tcphdr* tcp = (struct tcphdr* )(ip+1); printf("======== TCP HEADER ========\n"); printf("Source Port = %u\n", ntohs(tcp->source)); printf("Destination Port = %u\n", ntohs(tcp->dest)); printf("Sequence Number = %lu\n", ntohs(tcp->seq)); printf("Ack Sequence Number = %lu\n", ntohs(tcp->ack_seq)); printf("Data Offset = %u\n", tcp->doff); printf("FIN|SYN|RST|PSH|ACK|URG = %u%u%u%u%u%u\n", tcp->fin, tcp->syn, tcp->rst, tcp->psh, tcp->ack, tcp->urg); printf("Window Size = %u\n", ntohs(tcp->window)); printf("Check-Sum = 0x%04x\n", ntohs(tcp->check)); printf("Urgent Pointer = 0x%04x\n", ntohs(tcp->urg_ptr)); } } printf("\n"); frame_cnt++; if (frame_cnt >= FRAME_COUNT_MAX) { goto end; } } } } } end: close(sfd_raw); close(epollfd); error_end: return 0; }
実行結果
実行してみるとこんな感じ。パケットキャプチャにはroot権限が必要ですのでsudo付きで実行します。
[user@vm1 packet]$ gcc packet.c [user@vm1 packet]$ sudo ./a.out frame:1 150 bytes recieved ======== ETHERNET HEADER ======== Destination MAC Address = 08:00:27:82:c7:20 Source MAC Address = 0a:00:27:00:00:10 Ethernet Type = 0x0800 ======== IP HEADER ======== Version = 4 IHL = 5(160 bytes) Type of Service = 0x00[00000000] Total Length = 136 bytes Identification = 2872 Flags = 0x40[000] TTL = 128 Protocol = 6 Check-Sum = 0xa580 Source Address = 192.168.100.1 Destination Address = 192.168.100.101 ======== TCP HEADER ======== Source Port = 50312 Destination Port = 22 Sequence Number = 14001 Ack Sequence Number = 55884 Data Offset = 5 FIN|SYN|RST|PSH|ACK|URG = 000110 Window Size = 254 Check-Sum = 0xda51 Urgent Pointer = 0x0000 frame:2 60 bytes recieved ======== ETHERNET HEADER ======== Destination MAC Address = 08:00:27:82:c7:20 Source MAC Address = 0a:00:27:00:00:10 Ethernet Type = 0x0800 ======== IP HEADER ======== Version = 4 IHL = 5(160 bytes) Type of Service = 0x00[00000000] Total Length = 40 bytes Identification = 2873 Flags = 0x40[000] TTL = 128 Protocol = 6 Check-Sum = 0xa5df Source Address = 192.168.100.1 Destination Address = 192.168.100.101 ======== TCP HEADER ======== Source Port = 50312 Destination Port = 22 Sequence Number = 14001 Ack Sequence Number = 55884 Data Offset = 5 FIN|SYN|RST|PSH|ACK|URG = 000010 Window Size = 254 Check-Sum = 0x4ecd Urgent Pointer = 0x0000 frame:3 60 bytes recieved ======== ETHERNET HEADER ======== Destination MAC Address = 08:00:27:82:c7:20 Source MAC Address = 0a:00:27:00:00:10 Ethernet Type = 0x0800 ======== IP HEADER ======== Version = 4 IHL = 5(160 bytes) Type of Service = 0x00[00000000] Total Length = 40 bytes Identification = 2874 Flags = 0x40[000] TTL = 128 Protocol = 6 Check-Sum = 0xa5de Source Address = 192.168.100.1 Destination Address = 192.168.100.101 ======== TCP HEADER ======== Source Port = 50312 Destination Port = 22 Sequence Number = 14001 Ack Sequence Number = 55884 Data Offset = 5 FIN|SYN|RST|PSH|ACK|URG = 000010 Window Size = 256 Check-Sum = 0x4917 Urgent Pointer = 0x0000 [user@vm1 packet]$
今回の例ではSSH(TCPの宛先ポート番号が22番)のセグメントが表示されています。
やってみると意外と簡単にできますね。
Linuxのnamespaceでネットワークを区切って遊ぶ
Linuxのnamaspace(名前空間)で遊んでみます。
Dockerとかコンテナを実現する技術で使われるアレですね。
お遊び内容
まず、下図のネットワーク構成を作ります。
・eth1,eth2は内部ネットワーク
・vlan100,vlan200はVLANタグ付きのインタフェース
・veth1, veth2はnamespace間をつなぐインタフェース
・br1,br2はブリッジ
VLAN単位でネットワークを区切って通信ができるのか確認してみます。
ルーター、L3スイッチのVRFみたいなものですね。
vlan100,vlan200はネットワークが違うのでIPアドレスが同じでも各々で通信が可能なはずです。
各種設定
まず、VM1,VM2を作成します。(ここではともにCentOS 7です)
内部ネットワークのインタフェースはプロミスキャスモードをすべて許可にしてから起動します。
(許可しないとnamespace側のMACアドレスが宛先MACになっているフレームを破棄してしまうため)
以下、各VMの設定です。
◆VM1
内部ネットワークのインタフェース名を構成図のeth1に合わせます。
[user@localhost ~]$ ip link show dev enp0s9 4: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000 link/ether 08:00:27:1d:b8:43 brd ff:ff:ff:ff:ff:ff [user@localhost ~]$ sudo ip link set dev enp0s9 down [user@localhost ~]$ sudo ip link set dev enp0s9 name eth1 [user@localhost ~]$ sudo ip link set dev eth1 up [user@localhost ~]$ ip link show dev eth1 4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000 link/ether 08:00:27:1d:b8:43 brd ff:ff:ff:ff:ff:
続いてnamespace, 各インタフェースを作成します。
namespaceはip netns add 作ります。
namespace上でコマンドを実行する場合はip netns exec "namespace" "実行したいcommand" となります。
[user@vm1 ~]$ # namespaceの作成 [user@vm1 ~]$ sudo ip netns add ns1 # namespaceの作成 [user@vm1 ~]$ sudo ip link add veth1 type veth peer name eth1 netns ns1 # namespace間インタフェースの作成 [user@vm1 ~]$ # ブリッジの作成 [user@vm1 ~]$ sudo brctl addbr br1 # ブリッジの作成 [user@vm1 ~]$ sudo brctl addif br1 veth1 # ブリッジにveth1を接続 [user@vm1 ~]$ sudo brctl addif br1 eth1 # ブリッジにeth1を接続 [user@vm1 ~]$ # VLANの作成 [user@vm1 ~]$ sudo ip netns exec ns1 ip link add link eth1 name vlan100 type vlan id 100 # vlan100の作成 [user@vm1 ~]$ sudo ip netns exec ns1 ip addr add 192.168.0.1/24 dev vlan100 # vlan100にIPアドレス設定 [user@vm1 ~]$ sudo ip link add link eth1 name vlan200 type vlan id 200 # vlan200の作成 [user@vm1 ~]$ sudo ip addr add 192.168.0.1/24 dev vlan200 # vlan200にIPアドレス設定 [user@vm1 ~]$ # インタフェースのUP [user@vm1 ~]$ sudo ip link set dev veth1 up # veth1のUP [user@vm1 ~]$ sudo ip link set dev eth1 up # eth1のUP [user@vm1 ~]$ sudo ip link set dev br1 up # br1のUP [user@vm1 ~]$ sudo ip netns exec ns1 ip link set dev eth1 up # ns1のeth1をUP [user@vm1 ~]$ sudo ip netns exec ns1 ip link set dev vlan100 up # vlan100のUP [user@vm1 ~]$ sudo ip link set dev vlan200 up # vlan200のUP
namespaceと各インタフェースが作成されていることを確認します。
[user@vm1 ~]$ ip netns list ns1 [user@vm1 ~]$ [user@vm1 ~]$ ip addr show ... valid_lft forever preferred_lft forever 4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br1 state UP qlen 1000 link/ether 08:00:27:1d:b8:43 brd ff:ff:ff:ff:ff:ff inet6 fe80::bac:439:3aa7:6261/64 scope link valid_lft forever preferred_lft forever 6: veth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br1 state UP qlen 1000 link/ether 3a:f0:e6:57:6b:16 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::38f0:e6ff:fe57:6b16/64 scope link valid_lft forever preferred_lft forever 7: br1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 08:00:27:1d:b8:43 brd ff:ff:ff:ff:ff:ff inet6 fe80::a00:27ff:fe1d:b843/64 scope link valid_lft forever preferred_lft forever 8: vlan200@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 08:00:27:1d:b8:43 brd ff:ff:ff:ff:ff:ff inet 192.168.0.1/32 scope global vlan200 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fe1d:b843/64 scope link valid_lft forever preferred_lft forever [user@vm1 ~]$ sudo ip netns exec ns1 ip link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth1@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT qlen 1000 link/ether 26:5a:84:c9:33:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0 3: vlan100@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT qlen 1000 link/ether 26:5a:84:c9:33:03 brd ff:ff:ff:ff:ff:ff
◆VM2
VM2もVM1と同じように設定します。
[user@vm2 ~]$ # namespaceの作成 [user@vm2 ~]$ sudo ip netns add ns2 # namespaceの作成 [user@vm2 ~]$ sudo ip link add veth2 type veth peer name eth2 netns ns2 # namespace間インタフェースの作成 [user@vm2 ~]$ # ブリッジの作成 [user@vm2 ~]$ sudo brctl addbr br2 # ブリッジの作成 [user@vm2 ~]$ sudo brctl addif br2 veth2 # ブリッジにveth2を接続 [user@vm2 ~]$ sudo brctl addif br2 eth2 # ブリッジにeth2を接続 [user@vm2 ~]$ # VLANの作成 [user@vm2 ~]$ sudo ip netns exec ns2 ip link add link eth2 name vlan100 type vlan id 100 # vlan100の作成 [user@vm2 ~]$ sudo ip netns exec ns2 ip addr add 192.168.0.2/24 dev vlan100 # vlan100にIPアドレス設定 [user@vm2 ~]$ sudo ip link add link eth2 name vlan200 type vlan id 200 # vlan200の作成 [user@vm2 ~]$ sudo ip addr add 192.168.0.2/24 dev vlan200 # vlan200にIPアドレス設定 [user@vm2 ~]$ # インタフェースのUP [user@vm2 ~]$ sudo ip link set dev veth2 up # veth2のUP [user@vm2 ~]$ sudo ip link set dev eth2 up # eth2のUP [user@vm2 ~]$ sudo ip link set dev br2 up # br2のUP [user@vm2 ~]$ sudo ip netns exec ns2 ip link set dev eth2 up # ns2のeth2をUP [user@vm2 ~]$ sudo ip netns exec ns2 ip link set dev vlan100 up # vlan100のUP [user@vm2 ~]$ sudo ip link set dev vlan200 up # vlan200のUP
疎通確認
vlan200の通信確認
[user@vm1 ~]$ ping 192.168.0.2 [user@vm2 ~]$ sudo tcpdump -i vlan200
vlan100の通信確認
[user@vm1 ~]$ sudo ip netns exec ns1 ping 192.168.0.2 [user@vm2 ~]$ sudo ip netns exec ns2 tcpdump -i vlan100
期待通り通信ができることを確認できました。
参考
Technology: VRF & Linux Network Name Space
Linux Network Namespace で遊んでみる | CUBE SUGAR STORAGE
namespace を使ってみる - いますぐ実践! Linuxシステム管理 / Vol.260
proc経由でkernelとやり取りしてみる
procインタフェースを利用してユーザランドのアプリケーションとカーネルでデータをやりとりするサンプルです。
具体的には下記のようにechoでリダイレクトした文字列をカーネルで受信して、catで覗くとリダイレクトした文字列が表示されるような簡単なカーネルモジュールを作ります。開発環境はCentOS7です。
完成イメージ
$ echo "hello" > /proc/example $ cat /proc/example hello $
ソースコード
procmod.c
#include <linux/module.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/string.h> static char example[64] = {0}; static ssize_t example_write( struct file *filp, const char __user *buff, size_t user_len, loff_t *offset ) { size_t len = 0; if (0 != *offset) return -EINVAL; memset(example, 0, sizeof(example)); len = user_len > sizeof(example) ? sizeof(example) : user_len; memcpy(example, buff, len); example[len-1] = '\n'; printk(KERN_INFO "example: wrote - %s", example); return user_len; } /* /proc/exampleの内容を表示 */ static int example_show( struct seq_file *p, void *v) { seq_printf(p, "%s", example); printk(KERN_INFO "example: show - %s", example); return 0; } /* cat /proc/exampleで実行される */ static int example_open( struct inode *inode, struct file *file) { return single_open(file, example_show, NULL); } static const struct file_operations proc_example_operations = { .owner = THIS_MODULE, .open = example_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = example_write, }; /* insmod実行時に呼び出される */ static int proc_example_init(void) { proc_create("example", S_IRUGO | S_IWUGO, NULL, &proc_example_operations); printk(KERN_INFO "example: Module loaded, and created /proc/example\n"); return 0; } /* rmmod実行時に呼び出される */ static void proc_example_exit(void) { remove_proc_entry("example", NULL); printk(KERN_INFO "example: Module unloaded.\n"); } module_init(proc_example_init); module_exit(proc_example_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Example Kernel Module");
Makefile
obj-m := procmod.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
実行結果
ビルドしてカーネルモジュールをロードします。
$ make make -C /lib/modules/3.10.0-514.26.2.el7.x86_64/build M=/home/user/driver/proc modules make[1]: Entering directory `/usr/src/kernels/3.10.0-514.26.2.el7.x86_64' CC [M] /home/user/driver/proc/procmod.o Building modules, stage 2. MODPOST 1 modules CC /home/user/driver/proc/procmod.mod.o LD [M] /home/user/driver/proc/procmod.ko make[1]: Leaving directory `/usr/src/kernels/3.10.0-514.26.2.el7.x86_64' $ $ sudo insmod procmod.ko $ lsmod |grep procmod procmod 12702 0
ちゃんとアプリ、カーネル間でデータをやりとりできました。
次回は/dev/にスペシャルファイルを作ってアプリとカーネル間でepoll,ioctlを使った通信を試したいと思います。
さくらVPSでBitZenyを掘ってみる
さくらVPSでBitZenyのプールマイニングに挑戦してみます。まずはお試し1週間です。
はじめに
これまで、さくらVPSの空きリソースを使ってXMR(モネロ)という仮想通貨のプールマイニングをしていましたが、ここ最近の人気上昇を受けてか採掘量が段々と減ってきました(対JPY,BTCの価格もだいぶ上昇しましたし)。所詮空きリソースなのでこのままでも良いのですが、もう少しマイナーな仮想通貨で採掘難易度が低くかつ安定した数量を得られるものの方が面白いかな~と探していたところBitZenyという国産の仮想通貨にたどり着きました。
全体の流れ
大まかな流れは下記になります。
- walletの作成
- マイナープールにアカウント作成
- ワーカーの作成
- マイナーソフトの導入と実行
- あとは待つだけ
walletの作成
まずはBitZeny用のwalletを作成します。
作成時にニーモニックフレーズを選択します。 とりあえず注意書きにある通り、後で分かるように画面キャプチャとるなどして保管しておけばOK。
処理を進めるとwalletが出来上がります。
ページの下方にあるwalletのアドレスを後ほど使用します。
マイナープールにアカウント作成
BitZenyのマイナープールはいくつかありますが、今回アカウントを作成するのは"BitZeny Mining Pool - 大人の自由研究”です。 最初はLA BitZeny Poolにしようかと思ったのですが、最近のBitZeny高騰をうけて人気化した事とLA BitZeny Poolでのマイニングに関する記事が多いためかマイナーが1か所に偏っている状態でした。偏りがあると採掘効率が悪いようですのでこちらにしました。
BitZeny Mining Pool - 大人の自由研究 - Home
では、右上のメニューからSign Upを選択して新規アカウントを作成していきます。
アカウントが作成されたら、次はワーカーを作成します。
ワーカの作成
マイニングを行うにはワーカーなるものを作成する必要があります。
左メニューのMy AccountからMy Workerを選択します。
Add New Workerの所に任意の名前とパスワードを設定しワーカーを追加します。
ワーカーの名前とパスワードはuser, passwordのままでも構いません。
マイナーソフトの導入と実行
続いてマイナーソフトを採掘を行うマシンにインストールします。
まずは事前準備としてマイナーソフトを使うために必要なソフトを導入します。
$ sudo yum groupinstall "Development Tools" $ sudo yum install git libcurl-devel python-devel screen rsync
マイナーソフトのソースコードをダウンロードしてビルドします。
$ git clone https://github.com/bitzeny/cpuminer.git cpuminer $ cd cpuminer $ ./autogen.sh $ ./configure CFLAGS="-O3 -march=native -funroll-loops -fomit-frame-pointer" $ make $ sudo make install
マイナーソフトが用意できたらマイナープールのホームページの記載の通りにコマンドを実行します。
ログアウトしても処理を続けて欲しいのでnohupを付けておきます。
$ sudo nohup /usr/local/bin/minerd -a yescrypt -o stratum+tcp://ukkey3.space:3333 -u <ログインアカウント>.<ワーカー名> -p <ワーカーのパスワード> &
コマンドを実行してから数十分ほど待っているとDashBoardのハッシュレート等の採掘状況が表示されるようになります。
結果
ひとまず様子見で5日間でどれくらいマイニング報酬が得られるのか観測してみました。
大体1日あたり1.8 ZNY ぐらいは得られているでしょうか。
1.0 ZNYの相場が日本円で10円~40円なので20円/日の稼ぎって感じですね。
もちろん今後、価値が上がれば話は変わります(逆もしかり)。
参考
マイナーソフトの導入についてはこちらを参考にさせていただきました。
VBScriptでWordファイルのページ数一覧を作ってみる
ある日、大量のWordファイルで作られた資料の中身を目視確認する必要に迫られました。
複数人で分担して実施するのですがファイル毎にボリューム(ページ数)が異なるためファイル数で割り振ると不公平が発生します。
そこで事前にファイル単位のページ数がわかるといいなぁということでページ数の一覧を生成するスクリプトを作ってみました。
ソースコード
作成したスクリプトはスクリプトを実行したディレクトリ配下にあるWordファイルを検索しページ数を取得して結果をファイル出力します。
Wordファイルのページ数はBuiltinDocumentPropertiesで取ってこれます。
下記を参考にしました。
文字コードはSJISなので注意です。(UTF-8だと実行時に日本語を扱っている行で怒られます。)
' 'VBScript File 'Character code is SJIS ' Option Explicit Call Main() ' '@func Main '@brief メイン関数 ' Sub Main() 'ファイルシステムオブジェクトの生成 Dim fso Set fso = CreateObject("Scripting.FileSystemObject") 'カレントDIR配下にあるWordファイルのページ数を取得 Dim result result = GetNumberOfWordFilePageAll(fso, fso.GetFolder(".")) '結果をファイルに出力 Dim ret ret = WriteResultToFile(fso, "result.csv", result) End Sub ' '@func GetNumberOfWordFilePageAll '@brief カレントDIRにあるWordファイルのページ数を取得する '@param[in] dir ディレクトリパス '@param[in] fso ファイルシステムオブジェクト ' Function GetNumberOfWordFilePageAll(fso, dir) Dim result result= "" ' カレントDIRにあるwordファイルのページ数を数える Dim File For Each File in dir.Files result= result& GetNumberOfWordPages(fso, File.path) Next ' カレントDIR配下にあるDIRに対して再帰的に実行する Dim Subdir For Each Subdir in dir.SubFolders ' 再帰呼び出し result= result& GetNumberOfWordFilePageAll(fso, Subdir) Next GetNumberOfWordFilePageAll = result End Function ' '@func GetNumberOfWordPages '@brief Wordファイルのページ数を取得する '@param[in] fso ファイルシステムオブジェクト '@param[in] filepath 対象ファイル ' Function GetNumberOfWordPages(fso, filepath) '拡張子のチェック(wordファイル以外は対象外) Dim ExtName ExtName = UCase(fso.GetExtensionName(filepath)) If Not (ExtName = "DOC" OR ExtName = "DOCX") Then Exit Function End If ' wordファイルを開く Dim wordApp Set wordApp = WScript.CreateObject("Word.Application") Dim doc Set doc = wordApp.Documents.Open(filepath) ' ファイル名,ページ数を返す GetNumberOfWordPages = filepath & "," & doc.BuiltInDocumentProperties(14) & vbNewLine ' wordファイルを閉じる doc.Close Set doc = Nothing wordApp.Quit Set wordApp = Nothing End Function ' @func WriteResultToFile ' @brief 処理結果をファイルに出力 ' @param[in] fso ファイルシステムオブジェクト ' @param[in] filename 出力ファイル名 ' @param[in] result 処理結果 ' Function WriteResultToFile(fso, filename, result) Dim file Set file = fso.CreateTextFile("." & "\" & filename) Dim header header = UCase("ファイル名, ページ数") file.WriteLine(header) file.WriteLine(result) file.close Set file = Nothing MsgBox "処理結果を '" & filename & "' に出力しました。" End Function
実行結果
ちゃんと動作するか適当なワードファイルを用意して試してみます。
カレントにページ数が3のファイルを、サブディレクトリにページ数が1のファイルを作り配置します。
その他、Wordファイル以外を誤ってカウントしないか確認するためにダミーのテキストファイルを配置します。
こんな感じで配置しました。
ファイルの配置したらスクリプトをクリックして実行してみます。
ちゃんと動いてそうですね。
あとは記載内容のチェックもある程度自動化できればいいなぁ。
図とかは限界がありそうですが。
各社のAPIを使って仮想通貨の価格を表示してみる
以前はcoincheckのAPIだけ使っていましたが、仮想通貨の取引所も増えてきましたので他の取引所が提供しているAPIを試してみました。 pythonで仮想通貨の価格(日本円)表示します。
Zaif
APIの公式ドキュメントは下記。
ソースコード
#!/usr/local/bin/python # -*- coding: utf-8 -*- import requests import json coins = [ [1, 'BTC', 'btc_jpy'], [2, 'XEM', 'xem_jpy'], [3, 'MONA', 'mona_jpy'], ] urlbase = 'https://api.zaif.jp/api/1/last_price/' def main(): for i in range(len(coins)): response = requests.get(urlbase+coins[i][2]) if response.status_code != 200: raise Exception('return status code is {}'.format(response.status_code)) rate = json.loads(response.text) print("%-4s : \%-10s" % (coins[i][1], rate['last_price'])) if __name__ == "__main__": main()
実行結果
[user@localhost zaif]$ date Sun Nov 12 20:59:35 JST 2017 [user@localhost zaif]$ /usr/local/bin/python3 zaif.py BTC : \720000.0 XEM : \21.1 MONA : \305.7
bitflyer
APIの公式ドキュメントは下記。
ソースコード
#!/usr/local/bin/python # -*- coding: utf-8 -*- import requests import json coins = [ [1, 'BTC', 'btc_jpy'], [2, 'FX_BTC', 'fx_btc_jpy'], ] urlbase = 'https://api.bitflyer.jp/v1/getticker?product_code=' def main(): for i in range(len(coins)): response = requests.get(urlbase+coins[i][2]) if response.status_code != 200: raise Exception('return status code is {}'.format(response.status_code)) rate = json.loads(response.text) print("%-6s : \%-10s" % (coins[i][1], rate['ltp'])) if __name__ == "__main__": main()
実行結果
[user@localhost bitflyer]$ /usr/local/bin/python3 bitflyer.py BTC : \710919.0 FX_BTC : \717699.0
bitbank
APIの公式ドキュメントは下記。
ソースコード
#!/usr/local/bin/python # -*- coding: utf-8 -*- import requests import json coins = [ [1, 'BTC', 'btc_jpy'], [2, 'XRP', 'xrp_jpy'], [3, 'MONA', 'mona_jpy'], [4, 'BCC(BCH)','bcc_jpy'], ] urlbase = 'https://public.bitbank.cc/' def main(): for i in range(len(coins)): response = requests.get(urlbase+coins[i][2]+'/ticker') if response.status_code != 200: raise Exception('return status code is {}'.format(response.status_code)) result = json.loads(response.text) print("%-8s : \%-10s" % (coins[i][1], result['data']['last'])) if __name__ == "__main__": main()
実行結果
[user@localhost bitbank]$ date Sun Nov 12 21:18:20 JST 2017 [user@localhost bitbank]$ /usr/local/bin/python3 bitbank.py BTC : \718474 XRP : \22.762 MONA : \320.000 BCC(BCH) : \180365
BTCBOX
APIの公式ドキュメントは下記。
https://www.btcbox.co.jp/help/asm
ソースコード
#!/usr/local/bin/python # -*- coding: utf-8 -*- import requests import json ticker = 'https://www.btcbox.co.jp/api/v1/ticker/' def main(): response = requests.get(ticker) if response.status_code != 200: raise Exception('return status code is {}'.format(response.status_code)) rate = json.loads(response.text) print("BTC_JPY : \%-10s" % (rate['last'])) if __name__ == "__main__": main()
実行結果
[user@localhost btcbox]$ date Sun Nov 12 21:26:59 JST 2017 [user@localhost btcbox]$ /usr/local/bin/python3 btcbox.py BTC_JPY : \710030
kraken
APIの公式ドキュメントは下記。
ソースコード
#!/usr/local/bin/python # -*- coding: utf-8 -*- import requests import json coins = [ [1, 'XBT(BTC)', 'xbtjpy', 'XXBTZJPY'], [2, 'ETH', 'ethjpy', 'XETHZJPY'], ] urlbase = 'https://api.kraken.com/0/public/Ticker?pair=' def main(): for i in range(len(coins)): response = requests.get(urlbase+coins[i][2]) if response.status_code != 200: raise Exception('return status code is {}'.format(response.status_code)) rate = json.loads(response.text) print("%-8s : \%-10s" % (coins[i][1], rate['result'][coins[i][3]]['c'][0])) if __name__ == "__main__": main()
実行結果
[user@localhost kraken]$ date Sun Nov 12 21:45:18 JST 2017 [user@localhost kraken]$ /usr/local/bin/python3 kraken.py XBT(BTC) : \714001.000 ETH : \34330.00000
まとめ
国内の取引所の差額なんかを一覧にしてみるのも面白そうですね。