20191026のLinuxに関する記事は11件です。

[メモ]Linuxのディレクトリ構造

Linuxを理解する上でファイルシステムの理解は欠かせないと思いましたが、似たようなディレクトリが色々あって混乱しそうだったので書籍や記事を見ながらまとめました。

ディレクトリ 役割
/bin Linuxの基本コマンドが配置
/dev デバイスファイルが配置。システム起動時に接続されているデバイスがチェックされ、自動的に作成される
/etc システム管理用の設定ファイルや各種ソフトウェアの設定ファイルが配置
/lib /binや/sbinなどに置かれたコマンドやプログラムが利用するライブラリが配置
/lib/modules (ローダブル)カーネルモジュールが配置
/media CD/DVDなどのデータが配置
/opt Linuxインストール後、追加でインストールしたパッケージ(ソフトウェア)が配置
/proc カーネルやプロセスが保持する情報を配置
/root rootユーザーのホームディレクトリ
/sbin 主にシステム管理者が使用するコマンドが配置。オプションによって一般ユーザーも使用可能(rootユーザー以外はデフォルトでは使えない)
/tmp アプリケーションやユーザーが利用する一時ファイルが配置
/var システム運用中にサイズが変化するファイルが配置
/var/log システムやアプリケーションのログファイルが配置
/boot システム起動時に必要なブートローダー関連のファイルやカーネルイメージが配置
/usr ユーザーが共有するデータが配置。ユーティリティ、ライブラリ、コマンドなど
/usr/bin 一般ユーザー、管理者が使用するコマンドが配置
/usr/lib 各種コマンドが利用するライブラリが配置
/usr/sbin システム管理者のみ実行出来るコマンドが配置
/usr/local/bin 自分でインストールしたコマンドが配置
/home ユーザーのホームディレクトリが配置

出典①;本気で学ぶLinux実践入門
出典②:https://qiita.com/valzer0/items/67a4c8bf2b1be0fc825a

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デバッガを作りたいPart3

ブレークポイントを置きたい!!

ここではブレークポイントを置き任意の場所で動作を止めることを試みる。この動作の再開させるためには、ブレークをするための命令をもとに戻して、からプログラムカウンタを一つ戻してあげるということをする。

ブレークポイントについて

簡単にブレークポイントについて紹介する。
ここで行いたいのは特定のアドレスに本来ある命令から動作を一時停止する命令に一旦置き換えてブレークさせることが目的だ。

とりあえず命令を取ってきてみる。

ここでの目的は特定のアドレスの命令を取って来ることだ。
これが正常にできたら、あとはそこの命令を0xCCに置き換えればいいだけになる。
ここでやるのは、ripのアドレスにある命令を取ってくることだ。

tracer5.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/user.h>

void run_target(const char* target){
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execl(target, target, NULL);
}

void run_debugger(pid_t child_pid){
    int status;
    unsigned original_text;
    struct user_regs_struct regs;

    printf("I`m tracer and child process id is %d \n", child_pid);
    waitpid(child_pid, &status, 0);
    if (WIFEXITED(status)) {
        printf("program exited normally\n");
        exit(0);
    } else if (WIFSIGNALED(status)) {
        printf("terminated by signal %d\n", WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        printf("stopped by signal %d\n", WSTOPSIG(status));
    }
    ptrace(PTRACE_GETREGS, child_pid, 0, &regs);

    original_text = ptrace(PTRACE_PEEKTEXT, child_pid, regs.rip, 0);
    printf("rip: 0x%llx\n", regs.rip);
    printf("%x\n", original_text);


    ptrace(PTRACE_CONT, child_pid, NULL, NULL);

    waitpid(child_pid, &status, 0);
    if (WIFEXITED(status)) {
        printf("program exited normally\n");
        exit(0);
    } else if (WIFSIGNALED(status)) {
        printf("terminated by signal %d\n", WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        printf("stopped by signal %d\n", WSTOPSIG(status));
    }
}

int main (int argc, char **argv)
{
    pid_t child_pid;
    int status;

    if (argc < 1){
        printf("Usage: %s <target>\n",argv[0]);
    }

    child_pid = fork();

    if(child_pid == 0){
        //子プロセス
        printf("I`m tracee\n");
        run_target(argv[1]);
    }
    else if (child_pid > 0){
        //親プロセス
        run_debugger(child_pid);
    }
    else {
        printf("error at forking target.\n");
    }    
}

ここで注目すべきはPTRACE_GETREGSそしてPTRACE_PEEKTEXTだ。
29行目ではPTRACE_GETREGSによって子プロセスのすべてのレジスタの値を取得し、regs構造体に代入するということを行っている。regsは17行目で構造体の変数を宣言しているがこれはsys/user.hに入っている。
31行目ではPTRACE_PEEKTEXTによってregs.ripのアドレスに入っているプログラムの命令文を取ってきて、original_textに代入するということを行っていて、その下の2つのprintfでripの値とそのアドレスにある命令文を表示している。
以下が実行結果だ。

user@ubuntu:~/Desktop/tracer$ ./tracer hello
I`m tracer and child process id is 11173 
I`m tracee
stopped by signal 5
rip: 0x7f6cc4ed8090
e8e78948
Hello
program exited normally
user@ubuntu:~/Desktop/tracer$ 

六行目が五行目のrip上にある命令の内容みたいだ。何度か実行を繰り返してみるとプロセスIDとripの値が変わるが、命令の内容は変わらないので正常にうごいているみたいだ。

ブレークポイントを置いてみる

ブレークポイントをとりあえず、ripの場所においてみる。コードが長くなってきたので、コードの変更する部分だけ下に表す。
上のコードの32行目と33行目のprintfでripと命令を表示する部分を消して、そこに以下のものを代入してほしい。

tracer6.c
 if(original_text == 0xFFFFFFFF){
        printf("faied.\n");
    }else{
    printf("rip: 0x%016llx\n", regs.rip);
    printf("%016x\n", original_text);
    }

    ptrace(PTRACE_POKETEXT, child_pid, regs.rip, ((original_text & 0xFFFFFFFFFFFFFF00) | 0xCC));
    original_text = ptrace(PTRACE_PEEKTEXT, child_pid, regs.rip, 0);
    printf("%016x\n",original_text);

上で言う1行目から6行目はただ見やすくしただけだが、PTRACE_PEEKTEXTは正常に命令文を取ってこれないとき-1を返すのでそれをわかりやすくした。
重要なのは8行目で、PTRACE_POKETEXTは命令文を書き換える事ができるものだ。ここではripのアドレスの場所にある命令に0xccつまりint 3を入れるということをしていてこれは1バイトの命令でそこでブレークしてくれる。(SIGTRAPが発生する)ということでこの8行目がブレークポイントを設置する役割がある。9~10行目はそれができているかを確認できる。(0xCCが存在するかを確認するために書いた)そして、コード全体で見ればその下にPTRACE_CONTがあるので、そこで子プロセスが再開してブレークポイントまで進むが、この場合命令は一つしか進まない。(これを確認したいのであれば、ブレークしたあとにまたripの値を確認すれば見れるだろう。
というわけで、実行結果だ。

user@ubuntu:~/Desktop/tracer$ ./tracer hello
I`m tracer and child process id is 11295 
I`m tracee
stopped by signal 5
rip: 0x00007f2e2037a090
00000000e8e78948
e8e789cc
stopped by signal 5
user@ubuntu:~/Desktop/tracer$ 

ブレークポイントが置かれ、そこでブレークしたことによって、子プロセスはHelloを表示していないで止まっている。(だけどプロセスを確認したらこのプロセスIDのプロセスは存在しなかった。どうなったんだろう、、、)とにかく注目スべきはHelloが表示されていないかつ、signal 5が出ていることから、しっかりブレークしてくれたことがわかる。

ブレークポイントの再開

挨拶をしようと思って目を合わせたがやっぱり挨拶をしないのはちょっと気まずい。
ということで、ブレークしたあとにまた子プロセスを再開しHelloを表示させたい。
これを達成するためには、
* 命令をもとに戻してあげる。
* もう一度ブレークしたところの命令を実行させる、つまりripの値を一つ減らしてあげる。
* 動作を再開させる。
ということをすれば良い。

tracer7.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/user.h>

void run_target(const char* target){
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execl(target, target, NULL);
}

void run_debugger(pid_t child_pid){
    int status;
    unsigned original_text;
    struct user_regs_struct regs;
    unsigned long long int address;

    printf("I`m tracer and child process id is %d \n", child_pid);
    waitpid(child_pid, &status, 0);
    if (WIFEXITED(status)) {
        printf("program exited normally\n");
        exit(0);
    } else if (WIFSIGNALED(status)) {
        printf("terminated by signal %d\n", WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        printf("stopped by signal %d\n", WSTOPSIG(status));
    }
    //ここまでで子プロセスのPTRACE_TRACEMEによって子プロセスの動作が停止する。
    //レジスタの値を取ってくる。
    ptrace(PTRACE_GETREGS, child_pid, 0, &regs);
    //ripに入っているアドレスの命令を取ってくる。
    original_text = ptrace(PTRACE_PEEKTEXT, child_pid, regs.rip, 0);
    if(original_text == 0xFFFFFFFF){
        printf("faied.\n");
    }else {

    printf("rip: 0x%016llx\n", regs.rip);
    printf("%016x\n", original_text);
    }
    //int 3命令をripに入っているアドレスの命令に入れる。
    ptrace(PTRACE_POKETEXT, child_pid, regs.rip, ((original_text & 0xFFFFFFFFFFFFFF00) | 0xCC));

    //子プロセスの動作を再開させる。
    ptrace(PTRACE_CONT, child_pid, NULL, NULL);
    waitpid(child_pid, &status, 0);
    if (WIFEXITED(status)) {
        printf("program exited normally\n");
        exit(0);
    } else if (WIFSIGNALED(status)) {
        printf("terminated by signal %d\n", WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        printf("stopped by signal %d\n", WSTOPSIG(status));
    }
    //int 3によるブレークポイントで止まる。
    //ブレークポイントの行の命令をもとに戻したあとに
    //もう一度ブレークポイントの命令を外した命令を読み込ませたい。
    ptrace(PTRACE_GETREGS, child_pid, 0, &regs);
    regs.rip -= 1;
    //もとの命令を代入してあげる。
    ptrace(PTRACE_POKETEXT, child_pid, regs.rip, original_text);
    //レジスタも一つ元に戻したものを入れる。
    ptrace(PTRACE_SETREGS, child_pid, 0, &regs);

     original_text = ptrace(PTRACE_PEEKTEXT, child_pid, regs.rip, 0);
    if(original_text == 0xFFFFFFFF){
        printf("faied.\n");
    }else {

    printf("rip: 0x%016llx\n", regs.rip);
    printf("%016x\n", original_text);
    }
    //ブレークによって止まっていた子プロセスの再開
    ptrace(PTRACE_CONT, child_pid, NULL, NULL);
    waitpid(child_pid, &status, 0);
    if (WIFEXITED(status)) {
        printf("program exited normally\n");
        exit(0);
    } else if (WIFSIGNALED(status)) {
        printf("terminated by signal %d\n", WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        printf("stopped by signal %d\n", WSTOPSIG(status));
    }

}


int main (int argc, char **argv)
{
    pid_t child_pid;
    int status;

    if (argc < 1){
        printf("Usage: %s <target> \n",argv[0]);
    }

    child_pid = fork();

    if(child_pid == 0){
        printf("I`m tracee\n");
        run_target(argv[1]);
    }
    else if (child_pid > 0){

        run_debugger(child_pid);
    }
    else {
        printf("error at forking target.\n");
    }    
}

命令の内容を表示するところで本の命令の内容を変数original_text内に入れておいてあるのでそれをまた代入してあげればいい。が、PTRACE_POKETEXTのあとにPTRACE_PEEKTEXTを前のところでやってしまっているので。それをしないようにして、もとの命令をそのままにしておきたい。
といった感じでやってみたがうまく行かない..
実行結果だ。

user@ubuntu:~/Desktop/tracer$ ./tracer hello
I`m tracer and child process id is 2492 
I`m tracee
stopped by signal 5
rip: 0x00007fb70ff79090
00000000e8e78948
stopped by signal 5
rip: 0x00007fb70ff79090
00000000e8e78948
stopped by signal 11
user@ubuntu:~/Desktop/tracer$ 

ブレークの再開自体はできているみたいだが、signal 11で止まってしまっている。
signal 11はSegmentation faultみたいだ!!!!!
うゎーーーー
どうしたらいいのだろう。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Automotive Grade Linuxことはじめ (番外編)

社内のひと(ソフトウェア未経験者)に説明する機会があったので、スライドを作りました。

口頭原稿はありません。あしからず。また、情報は、guppyの時代のものです。


タイトルスライド

image.png


page1: What is Software? - ソフトウェアってなに?

image.png


Page2: How Software is Built - ソフトウェアはどうやってつくられるの?

image.png


Page 3: Fine Open Source Software in Smartphone

image.png


Page 4: Major Trend, How to Build & Sell Software - ソフトウェアビジネスのトレンド

image.png


Page 5: What happens Software in Vehicle - 車載ソフトウェアに起こっていること

image.png


Page 6: Automotive Grade Linux, What are They - Automotive Grade Linuxとは

image.png


Page 7: Automotive Grade Linux, Who Joined? - 構成メンバ

image.png


Page 8: Automotive Grade Linux, Hardware - ハードウェアの特徴

image.png


Page 9: Automotive Grade Linux, Operating System - OSの特徴

image.png


Page 10: Automotive Grade Linux, Releases in Detail - リリース情報

image.png

[EOF]

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デバッガを作りたいPart2

親プロセスをデバッガとして動かしてみる

ptraceを使う!

ptrace()の説明

対象のプログラムをデバッグするためにptraceシステムコールを使うこのシステムコールはgdbやstraceやradare2などにもつかわれているものです。
ptraceシステムコールは対象のプロセスを観察したりコントロールすること事ができる。このコントロールとはレジスタやメモリの値を書き換えたりできる。
今後デバッガプログラムをトレーサーデバッグ対象プログラムをトレーシーという名前で説明していく。
デバッグをする上でまずトレーサはトレーシーをアタッチする必要がある。マルチスレッドのトレーシーの場合はそれぞれでアタッチする必要があるそうだ。ptraceではアタッチの際プロセスIDが必要である。

とりあえずptrace()を使ってみる

とりあえず使ってみよう。
ということでまずはPTRACE_TRACEMEを使って「子プロセスの僕をトレースして!!!」と親プロセスにつたえる?ものを使ってみる。これを使うことによって、これ以降のexec()をを呼び出すたびにSIGTRAPが送信されるようだ、つまり
ここでは、helloを実行した直後に子プロセスの動作は一時停止する。
以下は具体的なコードだ。

tracer3.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ptrace.h>

void run_target(const char* target){
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execl(target, target, NULL);
}

int main (int argc, char **argv)
{
    pid_t child_pid;

    if (argc < 1){
        printf("Usage: %s <target>\n",argv[0]);
    }

    child_pid = fork();

    if(child_pid == 0){
        printf("I`m tracee\n");
        run_target(argv[1]);
    }
    else if (child_pid > 0){
        printf("I`m tracer and child process id is %d \n", child_pid);
    }
    else {
        printf("error at forking target.\n");
    }    
}

前回のコードに比べると対象プログラムをトレーサーのコマンドライン引数からし亭できるようになったことと7行目でptrace()を使っていることだ。
実行すると次のようになった。

user@ubuntu:~/Desktop/tracer$ ./tracer hello
I`m tracer and child process id is 7286 
user@ubuntu:~/Desktop/tracer$ I`m tracee

Helloが表示されてないことから、execl()を実行した直後に、子プロセスは一時停止していることがわかる。

止まっちゃったから動かす。

ここではさっき止めたトレーシーを再び動かしてみる。つかうのはPTRACE_CONTだ。これは、親プロセスで実行し、一時停止したトレーシーのプロセスをリスタートさせるptraceの機能だ。
下はとりあえずこれだけ実装して、うまく動かないコードだ。実行するのは控えたほうかいいかもしれない。

tracer4.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ptrace.h>

void run_target(const char* target){
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execl(target, target, NULL);
}

int main (int argc, char **argv)
{
    pid_t child_pid;

    if (argc < 1){
        printf("Usage: %s <target>\n",argv[0]);
    }

    child_pid = fork();

    if(child_pid == 0){
        printf("I`m tracee\n");
        run_target(argv[1]);
    }
    else if (child_pid > 0){
        printf("I`m tracer and child process id is %d \n", child_pid);
        ptrace(PTRACE_CONT, child_pid, NULL, NULL);
    }
    else {
        printf("error at forking target.\n");
    }    
}

そして、実行結果だ。

user@ubuntu:~/Desktop/tracer$ gcc tracer4.c -o tracer
user@ubuntu:~/Desktop/tracer$ ./tracer hello
I`m tracer and child process id is 7640 
user@ubuntu:~/Desktop/tracer$ I`m tracee

PTRACE_CONTによって一時停止されたHelloプログラムが再開されたはずだが、それができていない。その原因を考えてみてから進んでほしい。

原因

実行結果を見るとどうやら親プロセスが先に実行されているみたい。つまり親プロセスが実行するPTRACE_CONTが子プロセスのPTRACE_TRACEMEよりも前に実行されてしまっている。
どうすべきかというと親プロセスは子プロセスが一時停止するまで待たなければならない。また、実行しないほうがいいと推奨した理由は、親プロセスが止まっても子プロセスは、一時停止したままの状態になってしまっているために子プロセスがゾンビプロセスとして下記のように残ってしまうからだ。

user@ubuntu:~/Desktop/tracer$ ps -e | grep hello
  6761 ?        00:00:00 hello
  7286 ?        00:00:00 hello
  7640 ?        00:00:00 hello
user@ubuntu:~/Desktop/tracer$ 

何を使って待つか

いかにしてPTRACE_CONTの前にPTRACE_TRACEMEを実行させるか、ここでの最適解としてはPTRACE_CONTの前にwaitpid()を使ってみることにする。

waitpid()の使い方

waitpidは子プロセスのSIGTRAPを待つシステムコールだ。
とりあえず使用例だ。

tracer4.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

void run_target(const char* target){
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execl(target, target, NULL);
}

int main (int argc, char **argv)
{
    pid_t child_pid;
    int status;

    if (argc < 1){
        printf("Usage: %s <target>\n",argv[0]);
    }

    child_pid = fork();

    if(child_pid == 0){
        printf("I`m tracee\n");
        run_target(argv[1]);
    }
    else if (child_pid > 0){
        printf("I`m tracer and child process id is %d \n", child_pid);
        waitpid(child_pid, &status, 0);
        if (WIFEXITED(status)) {
            printf("program exited normally\n");
            exit(0);
        } else if (WIFSIGNALED(status)) {
            printf("terminated by signal %d\n", WTERMSIG(status));
        } else if (WIFSTOPPED(status)) {
            printf("stopped by signal %d\n", WSTOPSIG(status));
        }

        ptrace(PTRACE_CONT, child_pid, NULL, NULL);
    }
    else {
        printf("error at forking target.\n");
    }    
}

で実行結果だ。

user@ubuntu:~/Desktop/tracer$ ./tracer hello
I`m tracer and child process id is 8360 
I`m tracee
stopped by signal 5
user@ubuntu:~/Desktop/tracer$ Hello

waitpidでは、statusにint型の状態の情報を格納してくれて、以下のようなマクロによって上のように何が起きたのかを知ることができる。
* WEFIXITED
子プロセスが正常に終了した場合にtureを返す。
* WIFSIGNALED
子プロセスがシグナルによって終了した場合にtrueを返す。
* WIFSTOPPED
子プロセスがシグナルによって停止した場合にtrueを返す。

実行結果よりここでは、”stopped by signal 5”のあとにHelloがあることからwaitpidによって親プロセスが子プロセスの変化(ここでいうと停止)を待機し、子プロセスのPTRACE_TRACEMEによって動作が停止されたあとに、waitpidが反応し親プロセスの動作が再開され、statusの中身がマクロによって調査され、親プロセスのPTRACE_CONTによって子プロセスのが再開しHelloが表示された。

といった流れだ。またここでは親プロセスが終了した時点で、terminalがもとに戻っていて、そこで子プロセスが動いてしまっているために、$のあとにHelloが続いてしまっている。親プロセスは子プロセスを最後まで監視してゾンビにならないようにしたほうが親としての責任を果たせているからPTRACE_CONTのあとにもwaitpid()を使ったほうが好ましいだろう。

次回からブレークポイントを入れていきたいが今手こずっている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

間違いのないSSH Keysを最速で作成する

ssh-keyは鍵の強度など、なにかと気を遣うコマンドです。
また、普通に作成するとファイル名やパスフレーズを対話応答で聞かれるので少々面倒ですし、作業メモをとっている場合冗長になります。

2019年版という形にはなりますが、(Githubのドキュメントに従った)一般的と思われる強度を持った鍵を対話応答を省略するオプションを使って作成する方法です。

結論

ssh-keygenが使える環境で、<>内を変更して次のコマンドを入力します。
(コメントにはユーザ名やメールアドレスを入れる事が一般的と思います)

ssh-keygen -t rsa -b 4096 -C "<コメント>" -f <秘密鍵のファイル名> -N ""

何も聞かれることなく、直ちにカレントディレクトリに次のファイルが作成されます。
./<秘密鍵のファイル名>
./<秘密鍵のファイル名>.pub (公開鍵)

使用しているオプション

オプション 説明 備考
-t 鍵の種類を指定 rsa: 一般的、bit数は2048以上が必須
dsa: 古く脆弱なため非推奨
ecdsa, ed25519: rsaより短く安全だが古いクライアントでは使えないかもしれない
-b 鍵のbit数を指定 rsaなど可変長の鍵のbit数を指定する
Githubのドキュメントでは4096
-C 鍵に付与されるコメントを指定 空欄でも問題ないが、githubなどでは管理画面に表示されるので何か入れておいた方が便利
-f 作成する鍵のファイル名を指定 この名前で秘密鍵が、.pubの拡張子がついて公開鍵が作成される
-N パスフレーズを指定 秘密鍵にかけるパスワード。パスワードが分からないと認証処理ができないが、クラックする方法があり5文字程度だと数秒で解読されてしまうらしく、使う場合は11字以上が推奨(参考)。

環境

次の環境で確認しています。
OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5

経緯

よく見かけるページ

sshの鍵を作成する際に確認のためGoogle先生に聞くと次のサイトが良く出てきます。
お前らのSSH Keysの作り方は間違っている

鍵の強度はこの方法で十分と思いますが、鍵ファイルの保存場所やパスフレーズを聞かれて少し面倒です。

鍵ファイルの保存先応答をなくす

メモのしやすさなど考えるとオプションで指定したいです。
なので、次のサイトを参考にしてファイル名を指定するオプションを追加します。
ssh-keygenでファイル名を指定して鍵を生成する

パスフレーズを聞かれなくする

パスフレーズは必須と考えている方も多いと思いますが、個人的な用途などパスフレーズ無しの鍵を作る事もあると思います。作業メモに「エンターキーを2回叩く」などという言葉を書きたくありません。
なので、次のサイトを参考にして空のパスフレーズを指定します。
ssh-keygenをパスフレーズの確認なしで作成する

最後に

結果として、冒頭のコマンド構成が出来上がります。
鍵を作成するたびに上記の検索作業を繰り返していたので、メモを兼ねて一つの記事にしました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

間違いのないSSH鍵を最速で作成する

ssh-keygenは鍵の強度など、なにかと気を遣うコマンドです。
また、普通に作成するとファイル名やパスフレーズを対話応答で聞かれるので少々面倒ですし、作業メモをとっている場合冗長になります。

2019年版という形にはなりますが、(Githubのドキュメントに従った)一般的と思われる強度を持った鍵を対話応答を省略するオプションを使って作成する方法です。

結論

ssh-keygenが使える環境で、<>内を変更して次のコマンドを入力します。
(コメントにはユーザ名やメールアドレスを入れる事が一般的と思います)

ssh-keygen -t rsa -b 4096 -C "<コメント>" -f <秘密鍵のファイル名> -N ""

何も聞かれることなく、直ちにカレントディレクトリに次のファイルが作成されます。
./<秘密鍵のファイル名>
./<秘密鍵のファイル名>.pub (公開鍵)

使用しているオプション

オプション 説明 備考
-t 鍵の種類を指定 rsa: 一般的、bit数は2048以上が必須
dsa: 古く脆弱なため非推奨
ecdsa, ed25519: rsaより短く安全だが古いクライアントでは使えないかもしれない
-b 鍵のbit数を指定 rsaなど可変長の鍵のbit数を指定する
Githubのドキュメントでは4096
-C 鍵に付与されるコメントを指定 空欄でも問題ないが、githubなどでは管理画面に表示されるので何か入れておいた方が便利
-f 作成する鍵のファイル名を指定 この名前で秘密鍵が、.pubの拡張子がついて公開鍵が作成される
-N パスフレーズを指定 秘密鍵にかけるパスワード。パスワードが分からないと認証処理ができないが、クラックする方法があり5文字程度だと数秒で解読されてしまうらしく、使う場合は11字以上が推奨(参考)。

環境

次の環境で確認しています。
OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5

経緯

よく見かけるページ

sshの鍵を作成する際に確認のためGoogle先生に聞くと次のサイトが良く出てきます。
お前らのSSH Keysの作り方は間違っている

鍵の強度はこの方法で十分と思いますが、鍵ファイルの保存場所やパスフレーズを聞かれて少し面倒です。

鍵ファイルの保存先応答をなくす

メモのしやすさなど考えるとオプションで指定したいです。
なので、次のサイトを参考にしてファイル名を指定するオプションを追加します。
ssh-keygenでファイル名を指定して鍵を生成する

パスフレーズを聞かれなくする

パスフレーズは必須と考えている方も多いと思いますが、個人的な用途などパスフレーズ無しの鍵を作る事もあると思います。作業メモに「エンターキーを2回叩く」などという言葉を書きたくありません。
なので、次のサイトを参考にして空のパスフレーズを指定します。
ssh-keygenをパスフレーズの確認なしで作成する

最後に

結果として、冒頭のコマンド構成が出来上がります。
鍵を作成するたびに上記の検索作業を繰り返していたので、メモを兼ねて一つの記事にしました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【wget】Webサイトを丸ごとローカル環境にダウンロードする備忘録

はじめに

管理者画面にアクセスできなくなったワードプレスのサイトがあり、保存しておきたい場面がありました。その方法とハマりどころをまとめます。

環境

  • Mac OSX 10.15(19A602)

wgetの準備

Webサーバーからコンテンツをダウンロードするためのコマンドです。

$ brew install wget

Webサイトの一括ダウンロード

$ wget -rkp -l 3 http://example.com/
  • -r, --recursive: 再帰ダウンロードを行う
  • -k, --convert-links: HTML や CSS 中のリンクをローカルを指すように変更する
    • 変更されるのは全てのダウンロードが終了後に行われるので、ダウンロード中に確認しても適用されていない点に注意
  • -p, --page-requisites: HTML を表示するのに必要な全ての画像等も取得する
  • -l, --level=NUMBER: 再帰時の階層の最大の深さを NUMBER に設定する (0 で無制限)

課題

再起的に保存する.css.jsにURLパラメータが付いていることがあり、URLパラメータ付きの名前として保存されてしまっていた。保存時に?&はエスケープされてしまうためローカルHTML上からアクセスできない問題が発生した。

参照が外れている.css, .jsファイルをリネーム(?以下を削除)することで解決した。
(数が多くなかったので手動で対応したが、多い場合は正規表現などで置換できそう。)

試したが使わなかったコマンド

  • -H, --span-hosts: 再帰中に別のホストもダウンロード対象にする
    • あまり外部ドメインに依存していなかった
  • --content-disposition: Content-Dispositionで指定されたファイル名で保存する

最後に

-kの適用タイミングで結構ハマりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Bash】Shellの関数内だけsetコマンドオプションを有効化

普段Bashを使うことが多いので、よく使うコマンドとかワンコマンドで自動化したいものを関数化しています。関数のログとか未定義の変数を使用時にエラーメッセージを出力するようにしたかったのでsetコマンドを使用しています。ただそれを関数内だけ有効にしないと関数以外の普通のコマンドを実行した時も出力されてしまうので、setコマンドのオプションを無効化する必要がありました。
備忘録としてその方法を説明したいと思います。

setコマンドオプションの無効化

公式マニュアル

setコマンドオプションの無効化ですが、公式マニュアルには以下のように書かれていました。

Using + rather than - causes these options to be turned off.

-でオプションを指定して有効化しますが、+を使えばオプションを無効にすることができます。

方法

まずは-で有効化したいオプションを指定します。指定できるオプションはマニュアルを参照してください。

set -eux

3つのオプションを無効にするには+を使います。

set +eux

それぞれを関数の先頭行と最終行に記述すれば、関数内だけ実行コマンドのログやエラーメッセージを出力できるようになります。こんな感じに↓

function test {
    set -eux
    # Shellに設定されているオプションの確認
    # つまり、差分をとればsetで設定されたものが確認できる
    echo $SHELLOPTS
    echo "test"
    set +eux
    echo $SHELLOPTS
}

実行ログ

先頭にが付いているのがxオプションによって出力されたログになります。

+ echo braceexpand:emacs:errexit:hashall:histexpand:history:interactive-comments:monitor:nounset:xtrace
braceexpand:emacs:errexit:hashall:histexpand:history:interactive-comments:monitor:nounset:xtrace
+ echo test
test
+ set +eux
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

set -euxset +euxの差分は以下の3つのオプションです。

option description
errexit -eによって指定する
何かしらのコマンドがexit code0以外を出力した際に即座にerror exit
nounset -uによって指定する
未定義の変数の利用があればエラーメッセージを出力してerror exit
xtrace -xによって指定する
実行ログを出力する

:wave::wave:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Basic認証設定方法

Basic認証覚えたてホヤホヤのところで1日寝たら忘れてしまう人間なので不安が残った。
立て続けに設定する機会があったので忘れないようにメモる。

概要

検証環境などを一時的に関係者以外閲覧できないようにするための簡単な認証方法のひとつ。
簡単すぎる代償にセキュリティ面で問題があるらしいのでその点は頭の隅に入れておく。

設定方法

Basic認証の下準備

sudo su -
cd /etc/nginx/mod.d/

/etc配下のnginxディレクトリにはnginxの設定ファイルが格納されている。

cp wp.commonset{,.bak-yyyy-mm-dd}

編集前にバックアップを取っておく。大事な意識。

vim wp.commonset
------------------------------------------------------
[編集画面]
# auth_basic    "Secret Area";
# auth_basic_user_file  "/var/www/vhosts/.htpasswd";
↓
  auth_basic    "Secret Area";
  auth_basic_user_file  "/var/www/vhosts/.htpasswd";

上記のコメントアウトを外す。
こうすることで、これから.htpasswdに記載する情報を入力した場合にのみBasic認証を通り該当ページを閲覧可能になる。

IDとPassの作成

生成したIDと各種パスワードは控えておく。

.htaccess による認証用パスワード暗号化ツール
Basic認証用のパスワードを暗号化してくれるサイト。
IDは適当で自分がわかるものを考える。
スクリーンショット 2019-10-26 1.25.52.png
入力して生成後ちょっと下にいくと
スクリーンショット 2019-10-26 1.26.48.png
作ってもらえる。便利。

Password Generator
Passwordは以下の自動生成ツールを使用。
文字数16文字にチェックを入れてGenerateし好きなものを選ぶ。

vim /var/www/vhosts/.htpasswd
-------------------------------
[編集画面]
hogehoge:sK9MKFtk8yqkc

先ほど生成した暗号化されたIDとパスワードを.htpasswdファイルに記入。

nginxの確認

nginx -t

nginxの構文チェック

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

こんな感じで返ってきたらOK

番外編:WPの自動アップグレードを無効化

サーバ上でWordPressを動かしている場合
wp-config.phpを開き

define('AUTOMATIC_UPDATER_DISABLED', true);

をファイルの最下部に追記。
自動アップグレードを無効化して、「突然プラグインが使えなくなった」など今後起こる可能性があるトラブルを事前に潰しておく。

Basic認証が動いているか確認

検証環境のURLにアクセスし、Basic認証のログイン画面が表示されるか確認する。
IDとPassword Generatorで選んだ16文字のパスワードを入力してログインできれば完了。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linux&Rails環境構築の基礎知識

この記事では以下3つの内容を記載しています。

  • Linux
  • 環境構築
  • コマンド一覧

Linux

  1. ターミナルとは
  2. カーネルとシェル
  3. .bash_profile
  4. 環境変数
  5. 権限
  6. パーミッション

ターミナルとは

ターミナルとはパソコンの操作をするためのアプリ。
プログラミングで使うアプリみたいなイメージがあるがそうではなく本来はパソコンを操作するためのもの。
そうなるとパソコンの操作の仕方は2種類ある。
1. マウスを使っての操作。
2. ターミナルを使っての操作。

マウスを使っての操作

1つ目はおなじでみのマウスを使った操作。
例えばフォルダを作成する時、Macであればファインダーで右クリックでフォルダの作成を選んでフォルダを作成する。
つまりボタンをクリックしてパソコンに命令を与える。

ターミナルを使っての操作

次に2つ目がターミナルを使ったパソコン操作。
マウスでの操作に対して、ターミナルを使ったパソコンを操作する場合はコマンドでパソコンに命令を与えることになる。
例えばフォルダの中身を見たい時はlsコマンドを使ってパソコンにフォルダを中身を見せてくれと要求する。
他にもファイルを作成する時はtouchコマンドでファイルを作成してくれと命令する。
このようにコマンドで命令を与えるためのアプリがターミナルです。

カーネルとシェル

カーネルとはLinuxにおけるOSの根幹となるパーツのことで、実際に処理を行う部分のこと。
シェルはユーザーから送られてきたコマンドを受け取って、機械語に翻訳しカーネルに渡す役割がある。
処理をするのはカーネルです。
ではシェルを通さずにカーネルに直接コマンドで命令を与えればいいのでは?となります。
しかしそれはできません。
なぜならカーネルは機械語しか理解できないからです。
しかしコマンドは言い換えるなら人間がわかるようにしたもの、いわば人間語です。
なので人間語であるコマンドでカーネルに直接命令してもカーネルは命令が理解できません。
そこでシェルの登場、シェルはコマンドの意味も機械語もどちらも扱えます。
そのためコマンドを一度シェルに渡して機械語に翻訳してもらってカーネルに渡してもらう、こうすることで人間からパソコンに命令を与えることが可能となるわけです。

環境変数

環境変数はシェルに定義する変数のことです。
概念自体は普通の変数と同じで変数という箱に値を入れます。
ですが環境変数とは何かと聞かれると、シェルの設定をするためというのが正しい答えです。
どういうことかというと、シェルの設定は環境変数の中身の値によって設定することができます。
例えば、ホームディレクトリの設定はHOMEという環境変数を使います。

~ > ls      
bin/  cPractice/  Development/  test.rb*
~ > cd                                    # cdでホームディレクトリに移動
~ > pwd                                   # 現在のホームディレクトリが/home/mintであることを確認
/home/mint
~ > HOME=/home/mint/Development           # 環境変数HOMEに/home/mint/Developmentのパスを代入
/home/mint > cd                           # cdでホームディレクトリに移動
~ > pwd                                   # ホームディレクトリが/home/mint/Developmentに変わっていることを確認
/home/mint/Development

環境構築とは

環境構築とは開発をするための準備。
具体的には開発に必要なソフトをインストールすること。

  • homebrew
  • rbenv
  • mysql

homebrew

homevrewはソフトをインストールするソフト。
環境構築などでソフトをインストールする時はhomebrewを使ってインストールする。
以下はgitをインストール時の例

$ brew install git

そしてhomebrewはインストールしたソフトを入れておく箱の役割もある。
homebrewでインストールしたソフトは /usr/local/Cellar に保存される。
まとめるとhomebrewには2つの機能がある。
1. ソフトのインストール
2. インストールしたソフトを入れておく箱の役割

rbenv

rbenvの機能は3つ
1つ目はrubyのインストール
2つ目はrubyを保存する箱の役割
3つ目はrubyのバージョンの切り替え

コマンド一覧

■ファイル操作系

ファイル情報の詳細表示

$ ls -l

lsコマンドに-lオプションをつけることでファイルの詳細情報を表示できる。

ファイルの作成

$ touch test.rb

ファイルの削除

$ rm test.rb

フォルダの作成

$ mkdir test

フォルダの削除

$ rmdir test

フォルダの削除(フォルダの中身がある場合)

$ rm -rf test

ファイル名の変更

$ mv test.rb ruby.rb  # test.rbが変更したいファイル、ruby.rbが変更後の名前

ファイルの移動

$ mv test.rb test  # test.rbをtestフォルダに移動

ファイルの中身の表示

$ cat test.rb

ファイルを開く

$ open test.rb

補足: openコマンドでzipを解凍したり、アプリやURLを開くこともできる。

■Homebrew

インストール

$ brew install git

アンインストール

$ brew uninstall git

インストール済みソフトの一覧表示

$ brew list

インストール済みのソフトの検索

$ brew search git

brewに異常がないか調べる

$ brew doctor

■rbenv

rubyのインストール

$ rbenv install 2.3.1

rubyのアンインストール

$ ruby uninstall 2.3.1

インストール済みのrubyの一覧

$ rbenv versions

システム全体のrubyバージョンの切り替え

$ rbenv global 2.3.1

アプリごとのrubyバージョンの切り替え

$ rbenv local 2.3.1

■Mysql

mysqlの起動(mac)

$ sudo mysql.server start

mysqlの起動(ec2)

$ sudo service mysqld start

mysqlの停止(mac)

$ sudo mysql.server stop

mysqlの停止(ec2)

$ sudo service msyqld stop

rootユーザーでmysqlへのログイン

$ mysql -u root

rootユーザーでmysqlへのログイン(パスワードあり)

$ mysql -u root -p
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ubuntu 18.04 Railsインストールコマンド$ gem install rails のエラーを解決した話

目的

  • Ubuntu 18.04でコマンド$ gem install railsを実行した際に出たエラーを解決した話をまとめる。

経緯

  1. 下記記事を参考にUbuntu18.04でRailsの環境構築をしていたところ、コマンド$ gem install railsを実行した際にエラーが出た。
    MacOS / Ubuntu で Ruby on Rails の開発環境を構築する

エラー内容

  • 下記にエラー内容を抜粋した内容を記載する。
$ gem install rails
Building native extensions. This could take a while...
ERROR:  Error installing rails:
        ERROR: Failed to build gem native extension.

    current directory: /home/linuxbrew/.linuxbrew/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.4/ext/nokogiri
/home/linuxbrew/.linuxbrew/opt/ruby/bin/ruby -I /home/linuxbrew/.linuxbrew/Cellar/ruby/2.6.5/lib/ruby/2.6.0 -r ./siteconf20191025-8352-1g8if1f.rb extconf.rb
checking if the C compiler accepts ... yes
Building nokogiri using packaged libraries.
Using mini_portile version 2.4.0
checking for gzdopen() in -lz... no
zlib is missing; necessary for building libxml2
*** extconf.rb failed ***
・
・
・
extconf failed, exit code 1

Gem files will remain installed in /home/linuxbrew/.linuxbrew/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.4 for inspection.
Results logged to /home/linuxbrew/.linuxbrew/lib/ruby/gems/2.6.0/extensions/x86_64-linux/2.6.0/nokogiri-1.10.4/gem_make.out

分析

  • エラーの内容を翻訳して見た。
  • どうやらzlibなるものがインストールされていないようだ。
# 翻訳前
checking for gzdopen() in -lz... no
zlib is missing; necessary for building libxml2
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary

# 翻訳語
-lzでgzdopen()をチェックしています... no
zlibがありません。 libxml2のビルドに必要
*** extconf.rbが失敗しました***
何らかの理由でMakefileを作成できませんでした。おそらく必要なものがありません。

解決方法

  • zlibをインストールする為のコマンド$ sudo apt-get install zlib1g-devを実行した。
  • 実行後再度コマンド$ gem install railsを実行したら正常に実行された。
  • 下記に解決方法に至った筆者の流れを記載する。
    1. コマンド$ gem install railsを実行したらエラー発生した。
    2. コマンド$ sudo apt-get install zlib1g-devを実行した。
    3. コマンド$ gem install railsが正常に実行できた。

付録

  • 下記に筆者がコマンド$ gem install railsでエラーを出してから解決するまでのログを記載する。
miriwo@miriwoPC:~$ gem install rails
Building native extensions. This could take a while...
ERROR:  Error installing rails:
        ERROR: Failed to build gem native extension.

    current directory: /home/linuxbrew/.linuxbrew/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.4/ext/nokogiri
/home/linuxbrew/.linuxbrew/opt/ruby/bin/ruby -I /home/linuxbrew/.linuxbrew/Cellar/ruby/2.6.5/lib/ruby/2.6.0 -r ./siteconf20191025-8352-1g8if1f.rb extconf.rb
checking if the C compiler accepts ... yes
Building nokogiri using packaged libraries.
Using mini_portile version 2.4.0
checking for gzdopen() in -lz... no
zlib is missing; necessary for building libxml2
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/home/linuxbrew/.linuxbrew/Cellar/ruby/2.6.5/bin/$(RUBY_BASE_NAME)
        --help
        --clean
        --use-system-libraries
        --enable-static
        --disable-static
        --with-zlib-dir
        --without-zlib-dir
        --with-zlib-include
        --without-zlib-include=${zlib-dir}/include
        --with-zlib-lib
        --without-zlib-lib=${zlib-dir}/lib
        --enable-cross-build
        --disable-cross-build

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /home/linuxbrew/.linuxbrew/lib/ruby/gems/2.6.0/extensions/x86_64-linux/2.6.0/nokogiri-1.10.4/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /home/linuxbrew/.linuxbrew/lib/ruby/gems/2.6.0/gems/nokogiri-1.10.4 for inspection.
Results logged to /home/linuxbrew/.linuxbrew/lib/ruby/gems/2.6.0/extensions/x86_64-linux/2.6.0/nokogiri-1.10.4/gem_make.out
miriwo@miriwoPC:~$ sudo apt-get install zlib1g-dev
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages were automatically installed and are no longer required:
  libllvm7 linux-headers-4.18.0-17 linux-headers-4.18.0-17-generic linux-image-4.18.0-17-generic linux-modules-4.18.0-17-generic linux-modules-extra-4.18.0-17-generic
Use 'sudo apt autoremove' to remove them.
The following NEW packages will be installed:
  zlib1g-dev
0 upgraded, 1 newly installed, 0 to remove and 38 not upgraded.
Need to get 176 kB of archives.
After this operation, 457 kB of additional disk space will be used.
Get:1 http://jp.archive.ubuntu.com/ubuntu bionic/main amd64 zlib1g-dev amd64 1:1.2.11.dfsg-0ubuntu2 [176 kB]
Fetched 176 kB in 0s (453 kB/s)    
Selecting previously unselected package zlib1g-dev:amd64.
(Reading database ... 225859 files and directories currently installed.)
Preparing to unpack .../zlib1g-dev_1%3a1.2.11.dfsg-0ubuntu2_amd64.deb ...
Unpacking zlib1g-dev:amd64 (1:1.2.11.dfsg-0ubuntu2) ...
Setting up zlib1g-dev:amd64 (1:1.2.11.dfsg-0ubuntu2) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
miriwo@miriwoPC:~$ gem install rails
Building native extensions. This could take a while...
Successfully installed nokogiri-1.10.4
Successfully installed crass-1.0.5
Successfully installed loofah-2.3.1
Successfully installed rails-html-sanitizer-1.3.0
Successfully installed rails-dom-testing-2.0.3
Successfully installed builder-3.2.3
Successfully installed erubi-1.9.0
Successfully installed actionview-6.0.0
Successfully installed actionpack-6.0.0
Successfully installed activemodel-6.0.0
Successfully installed activerecord-6.0.0
Successfully installed globalid-0.4.2
Successfully installed activejob-6.0.0
Successfully installed mini_mime-1.0.2
Successfully installed mail-2.7.1
Successfully installed actionmailer-6.0.0
Building native extensions. This could take a while...
Successfully installed nio4r-2.5.2
Successfully installed websocket-extensions-0.1.4
Building native extensions. This could take a while...
Successfully installed websocket-driver-0.7.1
Successfully installed actioncable-6.0.0
Successfully installed mimemagic-0.3.3
Successfully installed marcel-0.3.3
Successfully installed activestorage-6.0.0
Successfully installed actionmailbox-6.0.0
Successfully installed actiontext-6.0.0
Successfully installed thor-0.20.3
Successfully installed method_source-0.9.2
Successfully installed railties-6.0.0
Successfully installed sprockets-4.0.0
Successfully installed sprockets-rails-3.2.1
Successfully installed rails-6.0.0
Parsing documentation for nokogiri-1.10.4
Installing ri documentation for nokogiri-1.10.4
Parsing documentation for crass-1.0.5
Installing ri documentation for crass-1.0.5
Parsing documentation for loofah-2.3.1
Installing ri documentation for loofah-2.3.1
Parsing documentation for rails-html-sanitizer-1.3.0
Installing ri documentation for rails-html-sanitizer-1.3.0
Parsing documentation for rails-dom-testing-2.0.3
Installing ri documentation for rails-dom-testing-2.0.3
Parsing documentation for builder-3.2.3
Installing ri documentation for builder-3.2.3
Parsing documentation for erubi-1.9.0
Installing ri documentation for erubi-1.9.0
Parsing documentation for actionview-6.0.0
Installing ri documentation for actionview-6.0.0
Parsing documentation for actionpack-6.0.0
Installing ri documentation for actionpack-6.0.0
Parsing documentation for activemodel-6.0.0
Installing ri documentation for activemodel-6.0.0
Parsing documentation for activerecord-6.0.0
Installing ri documentation for activerecord-6.0.0
Parsing documentation for globalid-0.4.2
Installing ri documentation for globalid-0.4.2
Parsing documentation for activejob-6.0.0
Installing ri documentation for activejob-6.0.0
Parsing documentation for mini_mime-1.0.2
Installing ri documentation for mini_mime-1.0.2
Parsing documentation for mail-2.7.1
Installing ri documentation for mail-2.7.1
Parsing documentation for actionmailer-6.0.0
Installing ri documentation for actionmailer-6.0.0
Parsing documentation for nio4r-2.5.2
Installing ri documentation for nio4r-2.5.2
Parsing documentation for websocket-extensions-0.1.4
Installing ri documentation for websocket-extensions-0.1.4
Parsing documentation for websocket-driver-0.7.1
Installing ri documentation for websocket-driver-0.7.1
Parsing documentation for actioncable-6.0.0
Installing ri documentation for actioncable-6.0.0
Parsing documentation for mimemagic-0.3.3
Installing ri documentation for mimemagic-0.3.3
Parsing documentation for marcel-0.3.3
Installing ri documentation for marcel-0.3.3
Parsing documentation for activestorage-6.0.0
Installing ri documentation for activestorage-6.0.0
Parsing documentation for actionmailbox-6.0.0
Installing ri documentation for actionmailbox-6.0.0
Parsing documentation for actiontext-6.0.0
Installing ri documentation for actiontext-6.0.0
Parsing documentation for thor-0.20.3
Installing ri documentation for thor-0.20.3
Parsing documentation for method_source-0.9.2
Installing ri documentation for method_source-0.9.2
Parsing documentation for railties-6.0.0
Installing ri documentation for railties-6.0.0
Parsing documentation for sprockets-4.0.0
Installing ri documentation for sprockets-4.0.0
Parsing documentation for sprockets-rails-3.2.1
Installing ri documentation for sprockets-rails-3.2.1
Parsing documentation for rails-6.0.0
Installing ri documentation for rails-6.0.0
Done installing documentation for nokogiri, crass, loofah, rails-html-sanitizer, rails-dom-testing, builder, erubi, actionview, actionpack, activemodel, activerecord, globalid, activejob, mini_mime, mail, actionmailer, nio4r, websocket-extensions, websocket-driver, actioncable, mimemagic, marcel, activestorage, actionmailbox, actiontext, thor, method_source, railties, sprockets, sprockets-rails, rails after 48 seconds
31 gems installed
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む